import os,sys,time
import struct
import serial
import binascii
import re

# 自研延时断电模块指令控制
class FX_PWR_CTL:
    def __init__(self,dev,bsp):
        self.init(dev,bsp)
        
    def __del__(self):
        self.ser.close()

    def init(self,dev,bsp):
        # 打开串口
        self.ser=serial.Serial(dev,bsp,
                            bytesize=serial.EIGHTBITS,    # 数据位：8位
                            parity=serial.PARITY_NONE,    # 校验位：无
                            stopbits=serial.STOPBITS_ONE, # 停止位：1位
                            timeout=None)                 # 读超时时间
        # 判断是否打开成功
        if self.ser:
            print("打开串口成功,串口详情参数：",self.ser)
        else:
            print("串口打开失败！")
    # 发送函数
    def send_command(self,command):
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        print(f"python发送:长度={result}，内容={command}",)
    # 接收函数
    def receive_results(self):
        return self.ser.readline().decode()

    def command_t(self,command):
        self.send_command(command)
        print("MCU:",self.receive_results())
    # 阻塞等待接收成功命令
    def blocking_data_receive(self,outtime):
        Start_time=time.time()
        # 判断是否在超时时间内
        while (time.time()-Start_time) < outtime:
            line=self.receive_results()
            print("MCU:",line)
            if 'successfully' in line:
                print("python : 接收成功！",line)
                break

    def crc16_ccitt(self,data):
        crc = 0xFFFF  # 初始值为0xFFFF
        for byte in data:
            crc ^= byte  # 异或运算
            for _ in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= 0xA001  # 多项式G(x) = x^16 + x^12 + x^5 + 1
                else:
                    crc >>= 1
        crc_hex = format(crc, '04x').upper()
        # 确保十六进制字符串是偶数长度
        if len(crc_hex) % 2 != 0:
            crc_hex = '0' + crc_hex
        # 将十六进制字符串转换为字节字符串
        crc_bytes = bytes.fromhex(crc_hex)

        # print("crc is：",crc,crc_hex,crc_bytes)
        return crc_bytes

    def int_to_two_byte_hex_str(self,val):
        # 将整数转换为二进制字符串，并确保至少有16位
        binary_str = bin(val)[2:].zfill(16)
        
        # 将二进制字符串转换为十六进制字符串
        hex_str = hex(int(binary_str, 2))[2:].upper()
        
        # 确保十六进制字符串是偶数长度，填充'0' if necessary
        if len(hex_str) % 2 != 0:
            hex_str = '0' + hex_str
        
        # 将十六进制字符串转换为字节字符串
        byte_str = bytes.fromhex(hex_str)
        
        return byte_str
    
    def int_to_two_byte_hex(self,value):
        # 将整数转换为十六进制字符串，并确保长度为4位
        hex_str = format(value, '04X')

        # 返回两个字节的十六进制字符串
        return hex_str

    # 获取版本
    def get_version(self):
        command = "version:?\r\n"
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        
        time.sleep(0.01)
        
        response = self.ser.read(self.ser.in_waiting)
        # print("response:",response)
        data_str = response.decode('utf-8')
        pattern = re.compile(r':(\S+)', re.DOTALL)
        match = pattern.search(data_str)
        
        if match:
            version = match.group(1)  # group(1) 返回第一个括号中匹配的内容
            return version
        else:
            return "No version found."
    # 获取电压
    def get_voltage(self):
        # 指令组包
        command = "voltage:?\r\n"
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        
        time.sleep(0.01)
        
        # 接收数据
        response = self.ser.read(self.ser.in_waiting)
        data_str = response.decode('utf-8')
        pattern = re.compile(r'=(\S+)', re.DOTALL)
        match = pattern.search(data_str)
        
        if match:
            ret = match.group(1)  # group(1) 返回第一个括号中匹配的内容
            return ret
        else:
            return None
        
    # 获取低电压阀值
    def get_low_batterye(self):
        # 指令组包
        command = "low_battery:?\r\n"
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        
        time.sleep(0.01)
        
        # 接收数据
        response = self.ser.read(self.ser.in_waiting)
        data_str = response.decode('utf-8')
        pattern = re.compile(r'=(\S+)', re.DOTALL)
        match = pattern.search(data_str)
        
        if match:
            ret = match.group(1)  # group(1) 返回第一个括号中匹配的内容
            return ret
        else:
            return None
        
    # 通知延时电源模块断电
    def notify_shutdown(self):
        # 指令组包
        command = "control:poweroff\r\n"
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        
        time.sleep(0.1)
        
        # 接收数据
        response = self.ser.read(self.ser.in_waiting)
        # print("接收：",response)
        ret="successfully" in response.decode("utf-8")
        if ret:
            return True
        else:
            return False
    # 电源类型获取,0是电源适配器，1是电池
    def get_powertype(self):
        # 指令组包
        command = "powertype:?\r\n"
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        
        time.sleep(0.01)
        
        # 接收数据
        response = self.ser.read(self.ser.in_waiting)
        data_str = response.decode('utf-8')
        pattern = re.compile(r':(\S+)', re.DOTALL)
        match = pattern.search(data_str)
        if match:
            ret = match.group(1)  # group(1) 返回第一个括号中匹配的内容
            return int(ret)
        else:
            return None
    # 延时时间获取
    def get_delay_time(self):
        # 指令组包
        command = "delay_time:?\r\n"
        hex_command = binascii.hexlify(command.encode()).decode('utf-8')
        bytes_command = binascii.unhexlify(hex_command)
        result=self.ser.write(bytes_command)
        
        time.sleep(0.01)

        # 接收数据
        response = self.ser.read(self.ser.in_waiting)
        data_str = response.decode('utf-8')
        pattern = re.compile(r'=(\S+)', re.DOTALL)
        match = pattern.search(data_str)
        if match:
            ret = match.group(1)  # group(1) 返回第一个括号中匹配的内容
            return float(ret)/1000
        else:
            return None

    # 函数：将数据分割成指定字节
    def split_bin_data_reverse(self,data,package_size):
        # 每包数据大小
        package_size = 64
        # 剩余数据大小
        remaining = len(data)
        # 结果列表
        packages = []
        # 循环直到数据处理完
        while remaining > 0:
            # 计算可以放入当前包的数据量
            package_data_size = min(remaining, package_size)
            # 切出当前包数据
            package_data = data[remaining - package_data_size: remaining]
            # 添加到结果列表
            packages.append(package_data)
            # 更新剩余数据大小
            remaining -= package_data_size
        # 返回数据包列表
        return packages
    
    def split_bin_data(self,data,package_size):
        chunks = [data[i:i + package_size] for i in range(0, len(data), package_size)]
        return chunks
    # 重传函数
    def retransmission(self,send_packet_data,mun,total_times):
        # 循环发送指定次数
        for i in range(total_times):
            result=self.ser.write(send_packet_data)
            print(f"python:重传:{i},第{mun}包，{result}")
            while True:
                line = self.ser.readline().decode().strip()
                print("MCU :",line)
                if 'successfully' in line:
                    print("python : 接收成功！",line)
                    break
                elif 'once more' in line:
                    break
    # OTA更新函数，使用原始app固件
    def OTA_update_bin(self,bin_file_path):
        # 添加文件头
        
        # 烧录
        pass
    # OTA更新函数
    def OTA_update(self,bin_file_path):
        # 数据头
        data_header = b'OTA_data:'
        data_package_size = 64
        retransmission_sign=0
        # 计算要发多少包，统计数量
        with open(bin_file_path, 'rb') as bin_file:
            # 读取文件总大小
            file_size = os.path.getsize(bin_file_path)
            # 处理数据包:分割成列表
            bin_data = bin_file.read()
            packer_data = self.split_bin_data(bin_data,64)
            # 计算需要发送的数据包数量
            num_packets = len(packer_data)
            print("python:bin文件分割包数:",num_packets)

            # 发送开始OTA总数
            command = "OTA_total="+str(num_packets)+"\r\n"
            self.send_command(command)
            self.blocking_data_receive(5)
            time.sleep(1)
            # 发送固件大小
            command = "OTA_size="+str(file_size)+"\r\n"
            self.send_command(command)
            self.blocking_data_receive(5)
            time.sleep(1)
            # 开始发送数据标志
            command = "OTA_state=start\r\n"
            self.send_command(command)
            self.blocking_data_receive(5)
            time.sleep(1)
            
            # 开始读取数据与发送
            iteration_count=0
            # for packet in reversed(packer_data):  # 从最后一个数据包开始反向遍历列表  # 增加迭代次数
            for packet in packer_data:
                print(f"python:第 {iteration_count}包")  # 打印当前迭代次数
                
                # 处理数据包（在这里，我们只是打印数据包的内容）
                crc = self.crc16_ccitt(packet)

                packet_send_data = data_header+\
                        struct.pack('>H', iteration_count)+\
                        struct.pack('>B', len(packet))+\
                        packet +\
                        crc+\
                        b"\r\n"
                
                result=self.ser.write(packet_send_data)
                # result=999
                print(f"python: num:{iteration_count},data_len:{len(packet)},type:{type(packet_send_data)},\len:{result}/{len(packet_send_data)},{packet_send_data.hex(sep=' ')}")# 
                
                # 收到回信后再继续下一包，否则显示上传失败
                while True:
                    line = self.ser.readline().decode().strip()
                    print("MCU :",line)
                    if 'successfully' in line:
                        print("python : 接收成功！",line)
                        break
                    elif 'once more' in line:
                        retransmission_sign=1
                        break
                # 需要执行重传
                if retransmission_sign == 1:
                    self.retransmission(packet_send_data,iteration_count,5)
                    retransmission_sign=0
                iteration_count += 1
                # time.sleep(0.2)
            
            # 结算打印
            print("##############################################")
            print("python:bin文件大小为：",file_size)
            print("python:bin文件分割包数：",num_packets)
            # 完成上传
            command = "OTA_state=end\r\n"
            self.send_command(command)
            print("MCU:",self.receive_results())
            time.sleep(1)
            while True:
                line = self.ser.readline().decode().strip()
                print("MCU :",line)
                if 'successfully' in line:
                    print("python : 接收成功！",line)
                    break
                elif 'Exit OTA' in line:
                    break

    # 将升级固件发送到单片机中(旧版)
    def OTA_update_old(self,bin_file_path):
        # 数据头
        data_header = b'OTA_data:'
        data_flag = 64

        # 计算要发多少包，统计数量
        with open(bin_file_path, 'rb') as bin_file:
            # 读取文件总大小
            file_size = os.path.getsize(bin_file_path)
            # 计算需要发送的数据包数量
            num_packets = file_size // data_flag + (file_size % data_flag > 0)

            # 发送开始OTA总数
            command = "OTA_total="+str(num_packets)+"\r\n"
            print(command)
            self.send_command(command)
            time.sleep(0.3)
            # 发送固件大小
            command = "OTA_size="+str(file_size)+"\r\n"
            print(command)
            self.send_command(command)
            time.sleep(0.3)

            # 实际的发包，收到OK后再发下一包
            try:
                bin_data = bin_file.read()
                # 读取文件内容，并按照64字节大小分割数据
                packet_number = num_packets
                # 发送数据
                for i in range(len(bin_data) - 64, len(bin_data), 64):   # 每次发送64字节
                    # 检查是否是最后一个数据块
                    if i + 64 >= len(bin_data):
                        data_length = len(bin_data) - i
                    else:
                        data_length = 64

                    crc = self.crc16_ccitt(bin_data[i:i+64])

                    # 打包数据
                    packet = data_header+\
                            struct.pack('>H', packet_number)+\
                            struct.pack('>B', data_length)+\
                            bin_data[i:i+64] +\
                            crc+\
                            b"\r\n"
                    print(packet.hex(sep=' '))
                    result=self.ser.write(packet)
                    
                    time.sleep(1)

                    # 接收回信
                    # while True:
                    #     line = self.ser.readline().decode().strip()
                    #     if 'successfully' in line:
                    #         print("接收成功！")
                    #         break
                    exit(0)
                    packet_number += 1

                while self.ser.in_waiting > 0:
                    self.ser.read()
            except Exception as e:
                print(f"An error occurred: {e}")
            finally:
                self.__del__()
    def test(self):
        # 发送开始OTA总数
        command = "OTA_total=999\r\n"
        self.send_command(command)
        self.blocking_data_receive(5)
        time.sleep(1)
        # 发送固件大小
        command = "OTA_size=1111\r\n"
        self.send_command(command)
        self.blocking_data_receive(5)
        time.sleep(1)
        # 开始发送数据标志
        command = "OTA_state=start\r\n"
        self.send_command(command)
        self.blocking_data_receive(5)
        time.sleep(1)
        
        while True:
            line = self.ser.readline().decode().strip()
            print("MCU :",line)
            if 'successfully' in line:
                print("接收成功！")
                break
            elif 'OTA out time !!!' in line:
                print("python:OTA流程超时！")
                break
            
    def set_delay_power_off_time(self,time):
        if isinstance(time, int):
            if 15000 <= time <= 65535:
                command = 'delay_time='+str(time)
                hex_command = binascii.hexlify(command.encode()).decode('utf-8')
                bytes_command = binascii.unhexlify(hex_command)
                result=self.ser.write(bytes_command)
                # # 接收数据
                # print(self.receive_results())
            else:
                print("值超出范围！！")
        else:
            print("输入的不是数字！！！")
            
    def set_low_battery(self,voltage_threshold):
        if isinstance(voltage_threshold, int):
            if 11000 <= voltage_threshold <= 65535:
                command = 'low_battery='+str(voltage_threshold)
                self.send_command(command)
            else:
                print("值超出范围！！")
        else:
            print("输入的不是数字！！！")